 # Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it 
# under the terms of the GNU Lesser General Public License as published by 
# the Free Software Foundation, either version 3 of the License, or 
# (at your option) any later version. This library is distributed in the 
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.

import os
import shutil
import logging

from studiovendor.Qt import QtWidgets

import mutils
import mutils.gui

try:
	import maya.cmds
	import maya.cmds as cmds
	import maya.mel as mel
except ImportError:
	import traceback
	traceback.print_exc()

logger = logging.getLogger(__name__)


MIN_TIME_LIMIT = -10000
MAX_TIME_LIMIT = 100000
DEFAULT_FILE_TYPE = "mayaBinary"  # "mayaAscii"

# A feature flag that will be removed in the future.
FIX_SAVE_ANIM_REFERENCE_LOCKED_ERROR = True


class PasteOption:

	Insert = "insert"
	Replace = "replace"
	ReplaceAll = "replace all"
	ReplaceCompletely = "replaceCompletely"


class AnimationTransferError(Exception):
	"""Base class for exceptions in this module."""
	pass


class OutOfBoundsError(AnimationTransferError):
	"""Exceptions for clips or ranges that are outside the expected range"""
	pass


def validateAnimLayers():
	"""
	Check if the selected animation layer can be exported.
	
	:raise: AnimationTransferError
	"""
	if maya.cmds.about(q=True, batch=True):
		return

	animLayers = maya.mel.eval('$gSelectedAnimLayers=$gSelectedAnimLayers')

	# Check if more than one animation layer has been selected.
	if len(animLayers) > 1:
		msg = "More than one animation layer is selected! " \
			  "Please select only one animation layer for export!"

		raise AnimationTransferError(msg)

	# Check if the selected animation layer is locked
	if len(animLayers) == 1:
		if maya.cmds.animLayer(animLayers[0], query=True, lock=True):
			msg = "Cannot export an animation layer that is locked! " \
				  "Please unlock the anim layer before exporting animation!"

			raise AnimationTransferError(msg)


def saveAnim(
		objects,
		path,
		time=None,
		sampleBy=1,
		fileType="",
		metadata=None,
		iconPath="",
		sequencePath="",
		bakeConnected=True
):
	"""
	Save the anim data for the given objects.

	Example:
		import mutils
		mutils.saveAnim(
			path="c:/example.anim", 
			objects=["control1", "control2"]
			time=(1, 20),
			metadata={'description': 'Example anim'}
			)
			
	:type path: str
	:type objects: None or list[str]
	:type time: (int, int) or None
	:type fileType: str or None
	:type sampleBy: int
	:type iconPath: str
	:type sequencePath: str
	:type metadata: dict or None
	:type bakeConnected: bool
	
	:rtype: mutils.Animation
	"""
	# Copy the icon path to the temp location
	if iconPath:
		shutil.copyfile(iconPath, path + "/thumbnail.jpg")

	# Copy the sequence path to the temp location
	if sequencePath:
		shutil.move(sequencePath, path + "/sequence")

	# Save the animation to the temp location
	anim = mutils.Animation.fromObjects(objects)
	anim.updateMetadata(metadata)
	anim.save(
		path,
		time=time,
		sampleBy=sampleBy,
		fileType=fileType,
		bakeConnected=bakeConnected
	)
	return anim


def clampRange(srcTime, dstTime):
	"""
	Clips the given source time to within the given destination time.

	Example:
		print clampRange((15, 35), (20, 30))
		# 20, 30

		print clampRange((25, 45), (20, 30))
		# 25, 30

	:type srcTime: (int, int)
	:type dstTime: (int, int)
	:rtype: (int, int)
	"""
	srcStart, srcEnd = srcTime
	dstStart, dstEnd = dstTime

	if srcStart > dstEnd or srcEnd < dstStart:
		msg = "The src and dst time do not overlap. " \
			  "Unable to clamp (src=%s, dest=%s)"
		raise OutOfBoundsError(msg, srcTime, dstTime)

	if srcStart < dstStart:
		srcStart = dstStart

	if srcEnd > dstEnd:
		srcEnd = dstEnd

	return srcStart, srcEnd


def moveTime(time, start):
	"""
	Move the given time to the given start time.

	Example:
		print moveTime((15, 35), 5)
		# 5, 20

	:type time: (int, int)
	:type start: int
	:rtype: (int, int)
	"""
	srcStartTime, srcEndTime = time
	duration = srcEndTime - srcStartTime

	if start is None:
		startTime = srcStartTime
	else:
		startTime = start

	endTime = startTime + duration

	if startTime == endTime:
		endTime = startTime + 1

	return startTime, endTime


def findFirstLastKeyframes(curves, time=None):
	"""
	Return the first and last frame of the given animation curves

	:type curves: list[str]
	:type time: (int, int)
	:rtype: (int, int)
	"""
	first = maya.cmds.findKeyframe(curves, which='first')
	last = maya.cmds.findKeyframe(curves, which='last')

	result = (first, last)

	if time:

		# It's possible (but unlikely) that the curves will not lie within the
		# first and last frame
		try:
			result = clampRange(time, result)
		except OutOfBoundsError as error:
			logger.warning(error)

	return result


def insertKeyframe(curves, time):
	"""
	Insert a keyframe on the given curves at the given time.

	:type curves: list[str]
	:type time: (int, int)
	"""
	startTime, endTime = time

	for curve in curves:
		insertStaticKeyframe(curve, time)

	firstFrame = maya.cmds.findKeyframe(curves, time=(startTime, startTime), which='first')
	lastFrame = maya.cmds.findKeyframe(curves, time=(endTime, endTime), which='last')

	if firstFrame < startTime < lastFrame:
		maya.cmds.setKeyframe(curves, insert=True, time=(startTime, startTime))

	if firstFrame < endTime < lastFrame:
		maya.cmds.setKeyframe(curves, insert=True, time=(endTime, endTime))


def insertStaticKeyframe(curve, time):
	"""
	Insert a static keyframe on the given curve at the given time.

	:type curve: str
	:type time: (int, int)
	:rtype: None
	"""
	startTime, endTime = time

	lastFrame = maya.cmds.findKeyframe(curve, which='last')
	firstFrame = maya.cmds.findKeyframe(curve, which='first')

	if firstFrame == lastFrame:
		maya.cmds.setKeyframe(curve, insert=True, time=(startTime, endTime))
		maya.cmds.keyTangent(curve, time=(startTime, startTime), ott="step")

	if startTime < firstFrame:
		nextFrame = maya.cmds.findKeyframe(curve, time=(startTime, startTime), which='next')
		if startTime < nextFrame < endTime:
			maya.cmds.setKeyframe(curve, insert=True, time=(startTime, nextFrame))
			maya.cmds.keyTangent(curve, time=(startTime, startTime), ott="step")

	if endTime > lastFrame:
		previousFrame = maya.cmds.findKeyframe(curve, time=(endTime, endTime), which='previous')
		if startTime < previousFrame < endTime:
			maya.cmds.setKeyframe(curve, insert=True, time=(previousFrame, endTime))
			maya.cmds.keyTangent(curve, time=(previousFrame, previousFrame), ott="step")


def duplicateNode(node, name):
	"""Duplicate the given node.

	:param node: Maya path.
	:type node: str
	:param name: Name for the duplicated node.
	:type name: str
	:returns: Duplicated node names.
	:rtype: list[str]
	"""
	if maya.cmds.nodeType(node) in ["transform", "joint"]:
		new = maya.cmds.duplicate(node, name=name, parentOnly=True)[0]
	else:
		# Please let us know if this logic is causing issues.
		new = maya.cmds.duplicate(node, name=name)[0]
		shapes = maya.cmds.listRelatives(new, shapes=True) or []
		if shapes:
			return [shapes[0], new]
	return [new]


def loadAnims(
	paths,
	spacing=1,
	objects=None,
	option=None,
	connect=False,
	namespaces=None,
	startFrame=None,
	mirrorTable=None,
	currentTime=None,
	showDialog=False,
):
	"""
	Load the animations in the given order of paths with the spacing specified.

	:type paths: list[str]
	:type spacing: int
	:type connect: bool
	:type objects: list[str]
	:type namespaces: list[str]
	:type startFrame: int
	:type option: PasteOption
	:type currentTime: bool
	:type mirrorTable: bool
	:type showDialog: bool
	"""
	isFirstAnim = True

	if spacing < 1:
		spacing = 1

	if option is None or option == "replace all":
		option = PasteOption.ReplaceCompletely

	if currentTime and startFrame is None:
		startFrame = int(maya.cmds.currentTime(query=True))

	if showDialog:

		msg = "Load the following animation in sequence;\n"

		for i, path in enumerate(paths):
			msg += "\n {0}. {1}".format(i, os.path.basename(path))

		msg += "\n\nPlease choose the spacing between each animation."

		spacing, accepted = QtWidgets.QInputDialog.getInt(
			None,
			"Load animation sequence",
			msg,
			spacing,
			QtWidgets.QInputDialog.NoButtons,
		)

		if not accepted:
			raise Exception("Dialog canceled!")

	for path in paths:

		anim = mutils.Animation.fromPath(path)

		if startFrame is None and isFirstAnim:
			startFrame = anim.startFrame()

		if option == "replaceCompletely" and not isFirstAnim:
			option = "insert"
		print("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
		print(path)
		anim.load(path)

		#duration = anim.endFrame() - anim.startFrame()
		#startFrame += duration + spacing
		#isFirstAnim = False


class Animation(mutils.Pose):

	IMPORT_NAMESPACE = "REMOVE_IMPORT"

	@classmethod
	def fromPath(cls, path):
		"""
		Create and return an Anim object from the give path.

		Example:
			anim = Animation.fromPath("/temp/example.anim")
			print anim.endFrame()
			# 14

		:type path: str
		:rtype: Animation
		"""
		anim = cls()
		anim.setPath(path)
		'''
		jesus 
		anim.read()
		'''
		return anim

	def __init__(self):
		mutils.Pose.__init__(self)

		try:
			timeUnit = maya.cmds.currentUnit(q=True, time=True)
			linearUnit = maya.cmds.currentUnit(q=True, linear=True)
			angularUnit = maya.cmds.currentUnit(q=True, angle=True)

			self.setMetadata("timeUnit", timeUnit)
			self.setMetadata("linearUnit", linearUnit)
			self.setMetadata("angularUnit", angularUnit)
		except NameError as msg:
			logger.exception(msg)

	def select(self, objects=None, namespaces=None, **kwargs):
		"""
		Select the objects contained in the animation.
		
		:type objects: list[str] or None
		:type namespaces: list[str] or None
		:rtype: None
		"""
		selectionSet = mutils.SelectionSet.fromPath(self.poseJsonPath())
		selectionSet.load(objects=objects, namespaces=namespaces, **kwargs)

	def startFrame(self):
		"""
		Return the start frame for anim object.

		:rtype: int
		"""
		return self.metadata().get("startFrame")

	def endFrame(self):
		"""
		Return the end frame for anim object.

		:rtype: int
		"""
		return self.metadata().get("endFrame")

	def mayaPath(self):
		"""
		:rtype: str
		"""
		mayaPath = os.path.join(self.path(), "animation.mb")
		if not os.path.exists(mayaPath):
			mayaPath = os.path.join(self.path(), "animation.ma")
		return mayaPath

	def poseJsonPath(self):
		"""
		:rtype: str
		"""
		return os.path.join(self.path(), "pose.json")

	def paths(self):
		"""
		Return all the paths for Anim object.

		:rtype: list[str]
		"""
		result = []
		if os.path.exists(self.mayaPath()):
			result.append(self.mayaPath())

		if os.path.exists(self.poseJsonPath()):
			result.append(self.poseJsonPath())

		return result

	def animCurve(self, name, attr, withNamespace=False):
		"""
		Return the animCurve for the given object name and attribute.

		:type name: str
		:type attr: str
		:type withNamespace: bool

		:rtype: str
		"""
		curve = self.attr(name, attr).get("curve", None)
		if curve and withNamespace:
			curve = Animation.IMPORT_NAMESPACE + ":" + curve
		return curve

	def setAnimCurve(self, name, attr, curve):
		"""
		Set the animCurve for the given object name and attribute.

		:type name: str
		:type attr: str
		:type curve: str
		"""
		self.objects()[name].setdefault("attrs", {})
		self.objects()[name]["attrs"].setdefault(attr, {})
		self.objects()[name]["attrs"][attr]["curve"] = curve

	def read(self, path=None):
		"""
		Read all the data to be used by the Anim object.

		:rtype: None
		"""
		path = self.poseJsonPath()

		logger.debug("Reading: " + path)
		mutils.Pose.read(self, path=path)
		logger.debug("Reading Done")

	def isAscii(self, s):
		"""Check if the given string is a valid ascii string."""
		return all(ord(c) < 128 for c in s)

	@mutils.unifyUndo
	@mutils.restoreSelection
	def open(self):
		"""
		The reason we use importing and not referencing is because we
		need to modify the imported animation curves and modifying
		referenced animation curves is only supported in Maya 2014+
		"""
		self.close()  # Make sure everything is cleaned before importing

		if not self.isAscii(self.mayaPath()):
			msg = "Cannot load animation using non-ascii paths."
			raise IOError(msg)

		nodes = maya.cmds.file(
			self.mayaPath(),
			i=True,
			groupLocator=True,
			ignoreVersion=True,
			returnNewNodes=True,
			namespace=Animation.IMPORT_NAMESPACE,
		)

		return nodes

	def close(self):
		"""
		Clean up all imported nodes, as well as the namespace.
		Should be called in a finally block.
		"""
		nodes = maya.cmds.ls(Animation.IMPORT_NAMESPACE + ":*", r=True) or []
		if nodes:
			maya.cmds.delete(nodes)

		# It is important that we remove the imported namespace,
		# otherwise another namespace will be created on next
		# animation open.
		namespaces = maya.cmds.namespaceInfo(ls=True) or []

		if Animation.IMPORT_NAMESPACE in namespaces:
			maya.cmds.namespace(set=':')
			maya.cmds.namespace(rm=Animation.IMPORT_NAMESPACE)

	def cleanMayaFile(self, path):
		"""
		Clean up all commands in the exported maya file that are
		not createNode.
		"""
		results = []

		if path.endswith(".mb"):
			return

		with open(path, "r") as f:
			for line in f.readlines():
				if not line.startswith("select -ne"):
					results.append(line)
				else:
					results.append("// End")
					break

		with open(path, "w") as f:
			f.writelines(results)

	@mutils.timing
	@mutils.unifyUndo
	@mutils.showWaitCursor
	@mutils.restoreSelection
	def save(
		self,
		path,
		time=None,
		sampleBy=1,
		fileType="",
		bakeConnected=True
	):
		"""
		Save all animation data from the objects set on the Anim object.

		:type path: str
		:type time: (int, int) or None
		:type sampleBy: int
		:type fileType: str
		:type bakeConnected: bool
		
		:rtype: None
		"""
		objects = list(self.objects().keys())

		fileType = fileType or DEFAULT_FILE_TYPE

		if not time:
			time = mutils.selectedObjectsFrameRange(objects)
		start, end = time

		# Check selected animation layers
		validateAnimLayers()

		# Check frame range
		if start is None or end is None:
			msg = "Please specify a start and end frame!"
			raise AnimationTransferError(msg)

		if start >= end:
			msg = "The start frame cannot be greater than or equal to the end frame!"
			raise AnimationTransferError(msg)

		# Check if animation exists
		"""
		jesus
		if mutils.getDurationFromNodes(objects or [], time=time) <= 0:
			msg = "No animation was found on the specified object/s! " \
				  "Please create a pose instead!"
			raise AnimationTransferError(msg)
		"""

		self.setMetadata("endFrame", end)
		self.setMetadata("startFrame", start)

		end += 1
		validCurves = []
		deleteObjects = []

		msg = u"Animation.save(path={0}, time={1}, bakeConnections={2}, sampleBy={3})"
		msg = msg.format(path, str(time), str(bakeConnected), str(sampleBy))
		logger.debug(msg)

		try:
			if bakeConnected:
				maya.cmds.undoInfo(openChunk=True)
				mutils.bakeConnected(objects, time=(start, end), sampleBy=sampleBy)

			for name in objects:
				if maya.cmds.copyKey(name, time=(start, end), includeUpperBound=False, option="keys"):
					dstNodes = duplicateNode(name, "CURVE")
					dstNode = dstNodes[0]
					deleteObjects.extend(dstNodes)

					if not FIX_SAVE_ANIM_REFERENCE_LOCKED_ERROR:
						mutils.disconnectAll(dstNode)

					# Make sure we delete all proxy attributes, otherwise pasteKey will duplicate keys
					mutils.Attribute.deleteProxyAttrs(dstNode)
					maya.cmds.pasteKey(dstNode)

					attrs = maya.cmds.listAttr(dstNode, unlocked=True, keyable=True) or []
					attrs = list(set(attrs) - set(['translate', 'rotate', 'scale']))

					for attr in attrs:
						dstAttr = mutils.Attribute(dstNode, attr)
						dstCurve = dstAttr.animCurve()

						if dstCurve:

							dstCurve = maya.cmds.rename(dstCurve, "CURVE")
							deleteObjects.append(dstCurve)

							srcAttr = mutils.Attribute(name, attr)
							srcCurve = srcAttr.animCurve()

							if srcCurve:
								preInfinity = maya.cmds.getAttr(srcCurve + ".preInfinity")
								postInfinity = maya.cmds.getAttr(srcCurve + ".postInfinity")
								curveColor = maya.cmds.getAttr(srcCurve + ".curveColor")
								useCurveColor = maya.cmds.getAttr(srcCurve + ".useCurveColor")

								maya.cmds.setAttr(dstCurve + ".preInfinity", preInfinity)
								maya.cmds.setAttr(dstCurve + ".postInfinity", postInfinity)
								maya.cmds.setAttr(dstCurve + ".curveColor", *curveColor[0])
								maya.cmds.setAttr(dstCurve + ".useCurveColor", useCurveColor)

							if maya.cmds.keyframe(dstCurve, query=True, time=(start, end), keyframeCount=True):
								self.setAnimCurve(name, attr, dstCurve)
								maya.cmds.cutKey(dstCurve, time=(MIN_TIME_LIMIT, start - 1))
								maya.cmds.cutKey(dstCurve, time=(end + 1, end + MAX_TIME_LIMIT))
								validCurves.append(dstCurve)

			fileName = "animation.ma"
			if fileType == "mayaBinary":
				fileName = "animation.mb"

			mayaPath = os.path.join(path, fileName)
			posePath = os.path.join(path, "pose.json")
			mutils.Pose.save(self, posePath)

			if validCurves:
				maya.cmds.select(validCurves)
				logger.info("Saving animation: %s" % mayaPath)
				maya.cmds.file(mayaPath, force=True, options='v=0', type=fileType, uiConfiguration=False, exportSelected=True)
				self.cleanMayaFile(mayaPath)

		finally:
			if bakeConnected:
				# HACK! Undo all baked connections. :)
				maya.cmds.undoInfo(closeChunk=True)
				maya.cmds.undo()
			elif deleteObjects:
				maya.cmds.delete(deleteObjects)

		self.setPath(path)
	@mutils.timing
	@mutils.showWaitCursor
	def load(self,path):
		print("---------------------------")
		print(path)
		for root, dirs, files in os.walk(path):
			if"sequence" in root and not ".mayaSwatches" in root:
				for file_name in os.listdir(root):
					file_path = os.path.join(root, file_name)
					if os.path.isfile(file_path):
						pass
		selectObject = cmds.ls(sl=1)
		if selectObject:
			Object_T = cmds.xform(selectObject[0] , q=1,translation=1, worldSpace=1)
			wave_Cv = self.Create_FX(file_path.replace("\\","/"))
			cmds.xform(wave_Cv,r = 1,translation=Object_T)
		else:
			self.Create_FX(file_path.replace("\\","/"))



	def Create_FX (self,Pic_Path):
		print(Pic_Path)
		Path = os.path.dirname(Pic_Path).split("/")[-1]
		Pic_Name = os.path.dirname(Pic_Path).split("/")[-1]
		ImgFilePath = Pic_Path
		PicPolyLens = []
		List_AllTransform = cmds.ls(transforms=1)
		for List_PolyS in List_AllTransform:
			if Pic_Name in List_PolyS:
				for Polys in cmds.listHistory(List_PolyS):
					if 'polyPlane' in Polys or 'polySphere' in Polys or 'polyCylinder' in Polys or 'polyCone' in Polys  or 'pTorusShape' in Polys or 'pTorus' in Polys:
						PicPolyLens.append(Polys)
		print(PicPolyLens)
		Pic_version = ''
		for i in range(len(PicPolyLens)):
			if i + 1 == len(PicPolyLens):
				Pic_version = str(i + 1) 

		polyP = cmds.polyPlane(n=Pic_Name + Pic_version,w = 1,h = 1 ,sx=10 ,sy = 10 ,ax=[0, 1 ,0] ,cuv = 2 ,ch=1)
		wave_FX = cmds.nonLinear( polyP[0],type ="wave")
		wave_FX_NonLinear = cmds.rename(wave_FX[0],Pic_Name + Pic_version +"_wave")
		wave_FX_Transform = cmds.rename(wave_FX[1],Pic_Name + Pic_version +"_waveHandle")
		main_A = cmds.circle(n=Pic_Name + 'main_A' + Pic_version, nr=[0, 1, 0], sw=360, d=3, r=1.5, ut=0, ch=0, tol=0.01)[0]
		main_B = cmds.circle(n=Pic_Name + 'main_B' + Pic_version, nr=[0, 1, 0], sw=360, d=3, r=1, ut=0, ch=0, tol=0.01)[0]

		polyP_Grp = cmds.group(em=True, name=Pic_Name + '_Grp' + Pic_version)


		lambertName = cmds.shadingNode('lambert', asShader=True, n=polyP[0] + '_labt')
		lambertSG = cmds.sets(renderable=1, noSurfaceShader=1, empty=1, name=polyP[0] + '_SG')
		cmds.connectAttr(lambertName + '.outColor', lambertSG + '.surfaceShader', f=1)
		cmds.sets(polyP[0], e=1, forceElement=lambertSG)
		shadingNodefile = cmds.shadingNode('file', asTexture=1, isColorManaged=1, n=polyP[0] + '_file')

		cmds.setAttr(shadingNodefile + '.fileTextureName', ImgFilePath, type="string")
		cmds.setAttr(shadingNodefile + '.useFrameExtension', 1)



		cmds.connectAttr(shadingNodefile + '.outColor', lambertName + '.color', f=1)
		TextureName = cmds.shadingNode('place2dTexture', asUtility=1, n=polyP[0] + '_Texture')
		Nodes = ['coverage', 'translateFrame', 'rotateFrame', 'mirrorU', 'mirrorV', 'stagger', 'wrapU',
				 'wrapV', 'repeatUV', 'offset', 'rotateUV', 'noiseUV', 'vertexUvOne', 'vertexUvTwo',
				 'vertexUvThree', 'vertexCameraOne']
		for Node in Nodes:
			cmds.connectAttr(TextureName + '.' + Node, shadingNodefile + '.' + Node, f=1)
		cmds.connectAttr(TextureName + '.outUV', shadingNodefile + '.uv')
		cmds.connectAttr(TextureName + '.outUvFilterSize', shadingNodefile + '.uvFilterSize')

		cmds.defaultNavigation(force=1, connectToExisting=1, source=shadingNodefile, destination=lambertName + '.color')
		cmds.connectAttr(shadingNodefile + '.outTransparency', lambertName + '.transparency', f=1)
		cmds.select(shadingNodefile, r=1)

			

		cmds.addAttr(main_A,ln='ShiJianPanYi', at='long', dv=0)
		cmds.addAttr(main_A,ln='BaoGuang', at='float', min=-5, max=5, dv=0)
		cmds.addAttr(main_A,ln='TouMingTongDao', at='float', min=0, max=2, dv=1)
		cmds.addAttr(main_A,ln='ColorGainR', at='float', min=0, max=20, dv=0)
		cmds.addAttr(main_A,ln='ColorGainG', at='float', min=0, max=20, dv=0)
		cmds.addAttr(main_A,ln='ColorGainB', at='float', min=0, max=20, dv=0)

		cmds.addAttr(main_A,ln ="wave",at ="enum",en = "--------------------------:")
		cmds.addAttr(main_A,ln ="offset",at ="double",min = -10 , max = 10,dv=0)
		cmds.addAttr(main_A,ln ="envelope",at ="double",min = 0 , max = 1,dv=1)
		cmds.addAttr(main_A,ln ="amplitude",at ="double",min = -5 , max = 5,dv=0.1)
		cmds.addAttr(main_A,ln ="wavelength",at ="double",min = 0.1 , max = 10,dv=1)
		cmds.addAttr(main_A,ln ="dropoff",at ="double",min = -1 , max = 1,dv=0)
		cmds.addAttr(main_A,ln ="dropoffPosition",at ="double",min = 0 , max = 1,dv=0)
		cmds.addAttr(main_A,ln ="minRadius",at ="double",min = 0 , max = 10,dv=0)
		cmds.addAttr(main_A,ln ="maxRadius",at ="double",min = 0 , max = 10,dv=1)



		cmds.setAttr (main_A + '.ShiJianPanYi', e=1, keyable=1)
		cmds.setAttr (main_A + '.BaoGuang', e=1, keyable=1)
		cmds.setAttr (main_A + '.TouMingTongDao', e=1, keyable=1)
		cmds.setAttr (main_A + '.ColorGainR', e=1, keyable=1)
		cmds.setAttr (main_A + '.ColorGainG', e=1, keyable=1)
		cmds.setAttr (main_A + '.ColorGainB', e=1, keyable=1)

		cmds.setAttr (main_A + ".wave",e =1,keyable =1 )
		cmds.setAttr (main_A + ".offset",e =1,keyable =1 )
		cmds.setAttr (main_A + ".envelope",e =1,keyable =1 )
		cmds.setAttr (main_A + ".amplitude",e =1,keyable =1 )
		cmds.setAttr (main_A + ".wavelength",e =1,keyable =1 )
		cmds.setAttr (main_A + ".dropoff",e =1,keyable =1 )
		cmds.setAttr (main_A + ".dropoffPosition",e =1,keyable =1 )
		cmds.setAttr (main_A + ".minRadius",e =1,keyable =1 )
		cmds.setAttr (main_A + ".maxRadius",e =1,keyable =1 )


		cmds.connectAttr(main_A + '.ShiJianPanYi', shadingNodefile + '.io', f=1)
		cmds.connectAttr(main_A + '.BaoGuang', shadingNodefile + '.exp', f=1)
		cmds.connectAttr(main_A + '.TouMingTongDao', shadingNodefile + '.ag', f=1)
		cmds.connectAttr(main_A + '.ColorGainR', shadingNodefile + '.cor', f=1)
		cmds.connectAttr(main_A + '.ColorGainG', shadingNodefile + '.cog', f=1)
		cmds.connectAttr(main_A + '.ColorGainB', shadingNodefile + '.cob', f=1)

		cmds.connectAttr(main_A+".offset",wave_FX_NonLinear + ".offset",f=1 )
		cmds.connectAttr(main_A+".envelope",wave_FX_NonLinear + ".envelope",f=1 )
		cmds.connectAttr(main_A+".amplitude",wave_FX_NonLinear + ".amplitude",f=1 )
		cmds.connectAttr(main_A+".wavelength",wave_FX_NonLinear + ".wavelength",f=1 )
		cmds.connectAttr(main_A+".dropoff",wave_FX_NonLinear + ".dropoff",f=1 )
		cmds.connectAttr(main_A+".dropoffPosition",wave_FX_NonLinear + ".dropoffPosition",f=1 )
		cmds.connectAttr(main_A+".minRadius",wave_FX_NonLinear + ".minRadius",f=1 )
		cmds.connectAttr(main_A+".maxRadius",wave_FX_NonLinear + ".maxRadius",f=1 )	


		cmds.setAttr(wave_FX_Transform + ".hiddenInOutliner",1)
		cmds.setAttr(wave_FX_Transform + ".visibility" ,0)

		cmds.parentConstraint (main_A,wave_FX_Transform,mo=1,weight =1)
		cmds.scaleConstraint (main_A,wave_FX_Transform,mo=1,weight =1)	
		cmds.parentConstraint(main_B, polyP[0])
		cmds.scaleConstraint(main_B, polyP[0], mo=1, weight=1)
		cmds.parent(main_B, main_A)
		cmds.parent(polyP[0], wave_FX_Transform,main_A, polyP_Grp)

		Attrs = ["tx","ty","tz","rx","ry","rz","sx","sy","sz","v"]
		for Attr in  Attrs:
			cmds.setAttr (polyP[0] +"."+ Attr ,lock= 1 )
			cmds.setAttr (polyP[0] +"."+ Attr ,k =0)
		mel.eval('evalDeferred( "showEditor sequence_file")')
		cmds.select(main_A)
		cmds.evalDeferred("mel.eval('ShowAttributeEditorOrChannelBox;')")

		return (main_A)

